/*
 *  @ProductName@
 *  Copyright (C) @CopyrightYear@
 *    National Institute of Advanced Industrial Science and Technology (AIST)
 *    Registration Number: @AISTRegistrationNumber@
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package jp.go.aist.six.vuln.core.scap.cpe.dictionary;

import jp.go.aist.six.util.IsoDate;
import jp.go.aist.six.vuln.model.scap.cpe.dictionary.CpeList;
import jp.go.aist.six.vuln.model.scap.cpe.dictionary.Generator;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;




/**
 * A data installer for the NVD CPE dictionary.
 *
 * @author  Akihito Nakamura, AIST
 * @version $Id: CpeDictionaryInstaller.java 256 2010-06-14 03:58:05Z nakamura5akihito $
 */
public class CpeDictionaryInstaller
{

    public static void main(
                    final String[] args
                    )
    {
        CpeDictionaryInstaller  installer = new CpeDictionaryInstaller();
        installer.execute( args );
    }



    private static final String[]  _START_MESSAGE_
    = new String[] {
            "NVD CPE Dictionary Installer, SIX VULN, version 0.9.0",
            "copyright (C) 2006-2010 Akihito Nakamura",
            "AIST Registered Number H18PRO-538"
    };



    public static final String  OFFICIAL_CPE_DICTIONARY_URL
    = "http://static.nvd.nist.gov/feeds/xml/cpe/dictionary/official-cpe-dictionary_v2.2.xml";



    private static final PrintStream  _STDOUT_ = System.out;

    /**
     */
    private static void _printMessage(
                    final String message
                    )
    {
        _STDOUT_.println( message );
    }



    /**
     * Constructor.
     */
    public CpeDictionaryInstaller()
    {
    }



    /**
     * Executes the installer.
     *
     * @param   args
     *  the command line arguments.
     * @throws Exception
     */
    public void execute(
                    final String[] args
                    )
    {
        _printStartMessage( _START_MESSAGE_ );

        List<String>  locations = _getSourceXmlLocations( args );

        _printSourceXmlLocations( locations );

        for (String  location : locations) {
            _printMessage( "*** install: " + location );
            try {
                _install( location );
            } catch (Exception ex) {
                _printMessage( "@@@ ERROR: " + location
                                + " - " + ex.getMessage() );
                _printMessage( "...skip" );
            }
        }
    }



    /**
     * Prints some message at the start.
     */
    private void _printStartMessage(
                    final String[] lines
                    )
    {
        _printMessage( "--------------------------------" );
        for (String  line : lines) {
            _printMessage( line);
        }
        _printMessage( "--------------------------------\n" );
    }



    /**
     */
    private void _printSourceXmlLocations(
                    final List<String> locations
                    )
    {
        _printMessage( "*** source XML to install: " );
        for (String  location : locations) {
            _printMessage( "  - " + location );
        }
        _printMessage( "\n" );
    }



    /**
     * Installs the XML Feed to the data store.
     * The location of the Feed is a file path or URL.
     *
     * @param   location
     *  the location of the XML Feed.
     * @return
     *  the persistent ID of the installed Nvd object.
     */
    private Object _install(
                    final String location
                    )
    throws Exception
    {
        if (location.startsWith( "http://" )
                        || location.startsWith( "https://" )
                        || location.startsWith( "file:/" )) {
            return _install( new URL( location ) );
        } else {
            File  file = new File( location );
            return _install( file );
        }
    }



    /**
     * Installs the XML Feed to the data store.
     *
     * @param   file
     *  the file containing the XML Feed.
     * @return
     *  the persistent ID of the installed Nvd object.
     */
    private Object _install(
                    final File file
                    )
    throws Exception
    {
        return _install( new FileInputStream( file ) );
    }



    /**
     * Installs the XML Feed to the data store.
     *
     * @param   url
     *  the URL of the XML Feed.
     * @return
     *  the persistent ID of the installed Nvd object.
     */
    private Object _install(
                    final URL url
                    )
    throws Exception
    {
        return _install( url.openStream() );
    }
    // It seems that download-and-install path is a little slower.
//    {
//        File  file = _downloadCpeDictionary( url );
//        return _install( new FileInputStream( file ) );
//    }



    /**
     * Saves URL content to a local temporary file.
     *
     * @throws IOException
     */
    private File _downloadCpeDictionary(
                    final URL url
                    )
    throws IOException
    {
        InputStream  in_stream = new BufferedInputStream( url.openStream() );
        File  file = File.createTempFile( "cpedic_", ".xml" );
        OutputStream  out_stream = new BufferedOutputStream( new FileOutputStream( file ) );

        byte[]  buffer = new byte[256];
        try {
            while (true) {
                int  size = in_stream.read( buffer );
                if (size == -1) {
                    break;
                }
                out_stream.write( buffer, 0, size );
            }
            out_stream.flush();
        } finally {
            try {
                in_stream.close();
            } catch (Exception ex) {
                //negligible
            }
            try {
                out_stream.close();
            } catch (Exception ex) {
                //negligible
            }
        }

        return file;
    }



    /**
     * Installs the XML Feed to the data store.
     *
     * @param   stream
     *  the stream which the XML Feed is read from.
     * @return
     *  the persistent ID of the installed Nvd object.
     */
    private Object _install(
                    final InputStream stream
                    )
    throws Exception
    {
        _printMessage( "unmarshalling XML..." );
        Object  obj = CpeDictionaryService.getXml().unmarshal( stream );
        _printMessage( "...done" );

        CpeList  dic = CpeList.class.cast( obj );
        Generator  gen = dic.getGenerator();
        if (gen != null) {
            String  pubDate = IsoDate.format( gen.getTimestamp() );
            _printMessage( "  - timestampe=" +  pubDate );
            _printMessage( "  - schema_version=" + gen.getSchemaVersion() );
            String  productName = gen.getProductName();
            _printMessage( "  - product_name=" + (productName == null ? "unknown" : productName) );
            String  productVersion = gen.getProductVersion();
            _printMessage( "  - product_version=" + (productVersion == null ? "unknown" : productVersion) );
        }

        _printMessage( "storing objects..." );
        Object  pid = CpeDictionaryService.getStore().createList( dic );
        _printMessage( "...done" );
        _printMessage( "  - PID=" + pid + "\n" );

        return pid;
    }



    /**
     * Returns the URL or file path of the XML specified by the command line arguments.
     */
    private List<String> _getSourceXmlLocations(
                    final String[] args
                    )
    {
        List<String>  locations = new ArrayList<String>();
        if (args.length == 0) {
            locations.add( OFFICIAL_CPE_DICTIONARY_URL );
        } else if (args.length == 1){
            File  file = new File( args[0] );
            _validateXmlFile( file );
            locations.add( file.getAbsolutePath() );
        } else {
            for (String  arg : args) {
                File  file = new File( arg );
                if (file.exists()) {
                    if (file.isFile()) {
                        _validateXmlFile( file );
                        locations.add( arg );
                    } else {
                        throw new IllegalArgumentException(
                                        "unsupported XML Feed file: " + arg );
                    }
                } else {
                    // Regard as URL.
                    try {
                        // URL validation
                        new URL( arg );
                        locations.add( arg );
                    } catch (MalformedURLException ex) {
                        throw new IllegalArgumentException(
                                        "malformed XML Feed URL: " + arg );
                    }
                }

            }
        }

        return locations;
    }



    /**
     * Validates the specified XML Feed file.
     * The file is valid if it exists, is a normal file, and can be read.
     */
    private void _validateXmlFile(
                    final File file
                    )
    {
        if (file.exists()  &&  file.isFile()  &&  file.canRead()) {
            // OK
        } else {
            throw new IllegalArgumentException(
                            "XML Feed file inexistent or read-protected: "
                            + file.getAbsolutePath() );
        }
    }

}
// CpeDictionaryInstaller

/* vim:set tabstop=4:set expandtab:set shiftwidth=4: */

